/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package com.sojoys.chess.game.echo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sojoys.artifact.plugin.netty.message.NettyMessage;
import com.sojoys.chess.game.plugin.SubmitServerInfoPlugin;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

/**
 * Sends one message when a connection is open and echoes back any received data
 * to the server. Simply put, the echo client initiates the ping-pong traffic
 * between the echo client and server by sending the first message to the
 * server.
 */
public final class EchoClient implements Runnable {
	static final Logger log = LoggerFactory.getLogger(SubmitServerInfoPlugin.class);

	String host;
	int port;
	boolean ssl;
	int reConnectCount;
	volatile boolean isChannelPrepared;
	EventLoopGroup group = null;
	Channel channel = null;
	OnStartupListener listener = null;
	volatile boolean shutdown = false;
	
	public EchoClient(String host, int port, boolean ssl,OnStartupListener listener) {
		super();
		this.host = host;
		this.port = port;
		this.ssl = ssl;
		this.listener = listener;
	}

	// static final boolean SSL = System.getProperty("ssl") != null;
	// static final String HOST = System.getProperty("host", "127.0.0.1");
	// static final int PORT = Integer.parseInt(System.getProperty("port",
	// "8007"));
	// static final int SIZE = Integer.parseInt(System.getProperty("size",
	// "256"));
	// private Executor executor = Executors.newScheduledThreadPool(1);

	public void connect(final String host, final int port, boolean ssl) throws Exception {
		// Configure SSL.git
		final SslContext sslCtx;
		if (ssl) {
			sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
		} else {
			sslCtx = null;
		}

		// Configure the client.
		group = new NioEventLoopGroup();
		final EchoClient client = this;
		try {
			Bootstrap b = new Bootstrap();
			b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
					.handler(new ChannelInitializer<SocketChannel>() {
						@Override
						public void initChannel(SocketChannel ch) throws Exception {
							ChannelPipeline p = ch.pipeline();
							if (sslCtx != null) {
								p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
							}
							// p.addLast(new LoggingHandler(LogLevel.INFO));
							p.addLast("codec", new EchoCodec());
							p.addLast(new EchoClientHandler());
						}
					});

			// Start the client.
			ChannelFuture f = b.connect(host, port).sync();
			f.addListener(new GenericFutureListener<Future<? super Void>>() {
				@Override
				public void operationComplete(Future<? super Void> future) throws Exception {
					if (future.isSuccess()) {
						reConnectCount = 0;
						isChannelPrepared = true;
						listener.onCompletion(true,client);
						log.info("与服务器{}:{}连接建立成功...", host, port);
					} else {
						isChannelPrepared = false;
						listener.onCompletion(false,client);
						log.info("与服务器{}:{}连接建立失败...", host, port);
					}
				}
			});
			channel = f.channel();
			f.channel().closeFuture().sync();
		} catch (Exception e) {
			isChannelPrepared = false;
			log.error("与服务器{}:{}连接出现异常...", host, port);
		} finally {
			isChannelPrepared = false;
			group.shutdownGracefully();
			reConnect(host, port);
		}

	}
	
	 // 断线重连  
    private void reConnect(String host, int port) {  
    	if(shutdown)
    		return;
        // fixme: 重连显式退出?  
        try {  
        	isChannelPrepared = false;  
            int delay = ++reConnectCount * 5;  
            reConnectCount = reConnectCount > 23 ? 23 : reConnectCount;  
            log.error("与服务器{}:{}连接已断开, {}秒后重连...", host, port, delay);  
  
            Thread.sleep(delay * 1000);  
            connect(host, port, ssl);
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  


	public void shutdown() {
		shutdown = true;
		group.shutdownGracefully();
		System.out.println("Netty Server Stop Success!");
	}

	@Override
	public void run() {
		try {
			connect(host, port, ssl);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	// 发送消息  
    public void sendMessage(NettyMessage msg) {  
        if (isChannelPrepared) {  
        	channel.writeAndFlush(msg);
        } else {  
            log.error("连接还未建立, 无法发送数据...");  
        }  
    }  
}